package com.eqs.enterprisequestionnaire.service;

import com.eqs.enterprisequestionnaire.model.pojo.Answer;
import com.eqs.enterprisequestionnaire.model.pojo.Question;
import com.eqs.enterprisequestionnaire.service.AnswerService;
import com.eqs.enterprisequestionnaire.service.HistoryService;
import com.eqs.enterprisequestionnaire.service.QuestionService;
import org.apache.commons.math3.linear.*;
import org.apache.commons.math3.stat.correlation.Covariance;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
import org.apache.commons.math3.special.Gamma;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.stream.Collectors;

import static com.eqs.enterprisequestionnaire.model.enums.QuestionEnum.*;

@Service
public class ValidityAnalysisService {

    @Autowired
    private QuestionService questionService;

    @Autowired
    private AnswerService answerService;
    @Autowired
    private HistoryService historyService;

    public boolean isValidityQuestion(Question question){
        //判断该题型是否可以进行效度分析
        String type = question.getType();
        return SINGLE_CHOICE.getType().equalsIgnoreCase(type) ||
                LEVELQUESTION.getType().equalsIgnoreCase(type) ||
                DROPDOWN.getType().equalsIgnoreCase(type);
    }
    public Map<Integer, double[]> getFactorLoadings(List<Integer> questionIds) {
        List<Question> questions = questionIds.stream()
                .map(questionService::selectByQuestionId)
                .filter(this::isValidityQuestion)
                .collect(Collectors.toList());

        double[][] dataMatrix = prepareDataMatrix(questions);
        RealMatrix matrix = new Array2DRowRealMatrix(dataMatrix);
        RealMatrix covarianceMatrix = new Covariance(matrix).getCovarianceMatrix();

        EigenDecomposition eigenDecomposition = new EigenDecomposition(covarianceMatrix);
        RealMatrix eigenVectors = eigenDecomposition.getV();

        Map<Integer, double[]> factorLoadings = new HashMap<>();
        for (int i = 0; i < eigenVectors.getColumnDimension(); i++) {
            double[] loadings = eigenVectors.getColumn(i);
            factorLoadings.put(i, loadings);
        }
        return factorLoadings;
    }

    public double calculateKMO(List<Integer> questionIds) {
        List<Question> questions = questionIds.stream()
                .map(questionService::selectByQuestionId)
                .filter(this::isValidityQuestion)
                .collect(Collectors.toList());

        double[][] dataMatrix = prepareDataMatrix(questions);
        RealMatrix matrix = new Array2DRowRealMatrix(dataMatrix);
        RealMatrix correlationMatrix = calculateCorrelationMatrix(matrix);

        return calculateKMO_M(correlationMatrix);
    }

    public double calculatePValue(List<Integer> questionIds) {
        List<Question> questions = questionIds.stream()
                .map(questionService::selectByQuestionId)
                .filter(this::isValidityQuestion)
                .collect(Collectors.toList());

        double[][] dataMatrix = prepareDataMatrix(questions);
        RealMatrix matrix = new Array2DRowRealMatrix(dataMatrix);
        RealMatrix correlationMatrix = calculateCorrelationMatrix(matrix);

        return calculateBartlettTest(correlationMatrix, dataMatrix.length);
    }

    private double[][] prepareDataMatrix(List<Question> questions) {
        List<Answer> allAnswers = questions.stream()
                .flatMap(q -> historyService.getListAnswerByQuestionId(q.getId()).stream())
                .collect(Collectors.toList());

        int n = allAnswers.size() / questions.size();

        double[][] dataMatrix = new double[n][questions.size()];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < questions.size(); j++) {
                Answer answer = allAnswers.get(i * questions.size() + j);
                dataMatrix[i][j] = convertAnswerToDouble(answer);
            }
        }
        return dataMatrix;
    }

    private double convertAnswerToDouble(Answer answer) {
        if (answer.getLevel() != null) {
            return answer.getLevel();
        } else if (answer.getContent() != null) {
            return Double.parseDouble(answer.getContent());
        }
        return 0.0;
    }

    private RealMatrix calculateCorrelationMatrix(RealMatrix matrix) {
        int n = matrix.getRowDimension();
        int m = matrix.getColumnDimension();
        RealMatrix correlationMatrix = MatrixUtils.createRealMatrix(m, m);

        for (int i = 0; i < m; i++) {
            for (int j = i; j < m; j++) {
                double corr = calculateCorrelation(matrix.getColumnVector(i).toArray(), matrix.getColumnVector(j).toArray());
                correlationMatrix.setEntry(i, j, corr);
                correlationMatrix.setEntry(j, i, corr);
            }
        }
        return correlationMatrix;
    }

    private double calculateCorrelation(double[] x, double[] y) {
        double meanX = new Mean().evaluate(x);
        double meanY = new Mean().evaluate(y);
        double covariance = 0.0;
        double varianceX = 0.0;
        double varianceY = 0.0;

        for (int i = 0; i < x.length; i++) {
            covariance += (x[i] - meanX) * (y[i] - meanY);
            varianceX += (x[i] - meanX) * (x[i] - meanX);
            varianceY += (y[i] - meanY) * (y[i] - meanY);
        }

        return covariance / Math.sqrt(varianceX * varianceY);
    }

    private double calculateKMO_M(RealMatrix correlationMatrix) {
        int m = correlationMatrix.getRowDimension();
        double[] aij = new double[m * (m - 1) / 2];
        double[] uij = new double[m * (m - 1) / 2];

        int index = 0;
        for (int i = 0; i < m; i++) {
            for (int j = i + 1; j < m; j++) {
                aij[index] = correlationMatrix.getEntry(i, j);
                uij[index] = partialCorrelation(correlationMatrix, i, j);
                index++;
            }
        }

        double sumAijSquared = 0;
        double sumUijSquared = 0;

        for (int i = 0; i < aij.length; i++) {
            sumAijSquared += aij[i] * aij[i];
            sumUijSquared += uij[i] * uij[i];
        }

        return sumAijSquared / (sumAijSquared + sumUijSquared);
    }

    private double partialCorrelation(RealMatrix correlationMatrix, int i, int j) {
        RealMatrix subMatrix = correlationMatrix.copy();
        subMatrix = subMatrix.getSubMatrix(new int[]{i, j}, new int[]{i, j});
        RealMatrix invSubMatrix = new LUDecomposition(subMatrix).getSolver().getInverse();
        return -invSubMatrix.getEntry(0, 1) / Math.sqrt(invSubMatrix.getEntry(0, 0) * invSubMatrix.getEntry(1, 1));
    }

    private double calculateBartlettTest(RealMatrix correlationMatrix, int n) {
        int p = correlationMatrix.getRowDimension();
        double det = new LUDecomposition(correlationMatrix).getDeterminant();
        double chiSquare = -(n - 1 - (2 * p + 5) / 6.0) * Math.log(det);
        return 1 - Gamma.regularizedGammaQ(p * (p - 1) / 2.0, chiSquare / 2.0);
    }
}